<template>
    <Teleport to="body" :disabled="!appendToBody">
        <Transition name="f-dialog-box">
            <div class="f-dialog-box" v-show="_hidden" @click.self="clickModalCloseDialog && _close()" @wheel="_wheel">
                <!-- 弹窗 -->
                <Transition name="f-dialog">
                    <div class="f-dialog" v-if="_hidden" @animationend="_animationend">
                        <!-- 标题 -->
                        <div class="header">
                            <slot name="header">
                                <span>{{ title }}</span>
                            </slot>
                            <span @click="_close" v-if="showClose">x</span>
                        </div>
                        <!-- 主体内容 -->
                        <slot></slot>
                        <!-- 底部 footer -->
                        <div class="footer">
                            <slot name="footer"></slot>
                        </div>
                    </div>
                </Transition>
            </div>
        </Transition>
    </Teleport>
</template>

<script setup lang="ts">
import { computed, ref } from "vue"
import type { DialogType } from "./f-dialog-type";

const props = withDefaults(defineProps<DialogType>(), {
    width: "50%",
    top: "15vh",
    lockScroll: true,
    showClose: true,
    clickModalCloseDialog: true
})

/** 执行回调 */
enum CALLBACK_LIST {
    打开的回调 = "open",
    打开动画结束时的回调 = "opened",
    关闭的回调 = "close",
    关闭动画结束时的回调 = "closed",
}

/** 弹窗状态 */
const _dialogVisable = defineModel<boolean>()   // 等价 defineEmits(["update:modelValue"])

const emit = defineEmits<{
    open: []
    opened: []
    close: []
    closed: []
}>()

const _close = () => {
    _dialogVisable.value = false    // 等价 emit("update:modelValue", false)
    _Run_Callback(CALLBACK_LIST.关闭的回调)
}

/** 工厂执行回调 */
const _Run_Callback = (callback: CALLBACK_LIST) => {
    emit(callback as any)
}

/** 动画是否结束 */
const _isAnimationend = ref(false)

/** body 元素 */
const _BodyElement = document.body

/** 是否隐藏 */
const _hidden = computed(() => {
    // dialog 打开时
    if (props.modelValue) {
        _Run_Callback(CALLBACK_LIST.打开的回调)
        _isAnimationend.value = false
        // 如果设置-滚动锁定 隐藏滚动条
        props.lockScroll && _BodyElement.classList.add("f-dialog--parent--hidden")
    } else {
        // 动画结束取消隐藏滚动条
        _isAnimationend.value && _BodyElement.classList.remove("f-dialog--parent--hidden")
    }
    return props.modelValue
})

// 动画结束
const _animationend = () => {
    // 打开时
    if (props.modelValue) {
        _Run_Callback(CALLBACK_LIST.打开动画结束时的回调)
    } else {
        _isAnimationend.value = true
        _Run_Callback(CALLBACK_LIST.关闭动画结束时的回调)
    }
}

/** 弹窗宽度 */
const _width = computed(() => {
    let width = props.width
    if (typeof width == "number" || !isNaN(Number(width))) {
        width = width + "px"
    }
    return width
})

const _wheel = (e: WheelEvent) => {
    // 阻止冒泡到父级
    props.lockScroll && e.stopPropagation()
}

</script>

<style lang="scss">
$padding: 20px;
// dialog 对话框宽度
$width: v-bind("_width");
// 遮罩层背景
$background: rgba(0, 0, 0, 0.4);
// 动画上移大小
$transform: translateY(-30px);
// 动画时间
$time: 400ms;

/** 遮罩层 */
/* 显示动画 */
@keyframes MASK_BAC_START {
    0% {
        background-color: rgba(0, 0, 0, 0);
    }

    100% {
        background-color: $background;
    }
}

/* 关闭动画 */
@keyframes MASK_BAC_END {
    0% {
        background-color: $background;
    }

    100% {
        background-color: rgba(0, 0, 0, 0);
    }
}

.f-dialog-box-enter-active {
    animation: MASK_BAC_START $time;
}

.f-dialog-box-leave-active {
    animation: MASK_BAC_END $time;
}

/** 弹窗 */
/* 离开动画 */
@keyframes DIALOG_SHOW_START {
    0% {
        transform: $transform;
        opacity: 0;
    }

    50%,
    100% {
        opacity: 1;
        transform: translateY(0px);
    }
}

/** 关闭动画 */
@keyframes DIALOG_SHOW_END {
    0% {
        transform: translateY(-0px);
        opacity: 1;
    }

    50%,
    100% {
        opacity: 0;
        transform: $transform;
    }
}

.f-dialog-enter-active {
    animation: DIALOG_SHOW_START $time;
}

.f-dialog-leave-active {
    animation: DIALOG_SHOW_END $time;
}

.f-dialog-box {
    position: fixed;
    right: 0;
    top: 0;
    left: 0;
    bottom: 0;
    z-index: 99999;
    overflow: auto;
    background-color: $background;

    /* 弹窗 */
    .f-dialog {
        margin: v-bind("props.top") auto 50px;
        box-sizing: border-box;
        width: $width;
        border-radius: 5px;
        padding: 0 $padding;
        background-color: white;

        /* header */
        .header {
            display: flex;
            justify-content: space-between;
            padding: $padding 0;
            font-size: 20px;

            & :last-child {
                color: #ABADB2;
                cursor: pointer;
            }
        }

        /* footer */
        .footer {
            padding: 50px 0 $padding ;
            text-align: right;
        }
    }
}
</style>